﻿using Se.Log;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace Se.Net.Sockets
{
    public class SocketClient
    {
        #region Event

        public event Action<ExSocket> Connected;
        private void OnConnected(ExSocket socket)
        {
            if (this.Connected != null) this.Connected(socket);
        }


        public event Action<ExSocket> DisConnected;
        private void OnDisConnected(ExSocket socket)
        {
            if (this.DisConnected != null) this.DisConnected(socket);
        }

        public event Action<byte[]> ReceiveData;
        private void OnReceiveData(byte[] receiveArgs)
        {
            if (this.ReceiveData != null) this.ReceiveData(receiveArgs);
        }

        #endregion

        private ExSocket _socket;
        private ConcurrentQueue<SocketAsyncEventArgs> _receiveEventArgsPool;
        private ConcurrentQueue<SocketAsyncEventArgs> _sendEventArgsPool;
        private EndPoint _endPoint;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="endPoint">Endpoint</param>
        /// <param name="receiveCount">同时接受数据的数量</param>
        /// <param name="sendCount">同时发送数据的数量</param>
        public SocketClient(IPEndPoint endPoint, int receiveCount, int sendCount)
        {
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            this._socket = new ExSocket(socket);
            this._endPoint = endPoint;
            socket.Bind(endPoint);

            this._receiveEventArgsPool = new ConcurrentQueue<SocketAsyncEventArgs>();
            for (var i = 0; i < receiveCount; i++)
            {
                var receiveArgs = new SocketAsyncEventArgs();
                var seBs = new byte[8024];
                receiveArgs.SetBuffer(seBs, 0, seBs.Length);
                receiveArgs.Completed += ReceiveEventArgs_Completed;
                receiveArgs.AcceptSocket = this._socket.Socket;
                this._receiveEventArgsPool.Enqueue(receiveArgs);
            }

            this._sendEventArgsPool = new ConcurrentQueue<SocketAsyncEventArgs>();
            for (var i = 0; i < sendCount; i++)
            {
                var sendArgs = new SocketAsyncEventArgs();
                var reBs = new byte[8024];
                sendArgs.SetBuffer(reBs, 0, reBs.Length);
                sendArgs.Completed += SendEventArgs_Completed;
                sendArgs.AcceptSocket = this._socket.Socket;
                this._sendEventArgsPool.Enqueue(sendArgs);
            }
        }

        public SocketClient(string address, int port, int receiveCount, int sendCount)
            : this(new IPEndPoint(IPAddress.Parse(address), port), receiveCount, sendCount)
        {
        }

        public bool Connect(IPEndPoint endPoint)
        {
            try
            {
                this._socket.Socket.Connect(endPoint);
                this.ProcessRecieve();
                return true;
            }
            catch (Exception ex)
            {
                SeLog.Error("SocketClient->Connect Can't connect SocketServer {0}, errorInfo {1}", endPoint.ToString(), ex.ToString());
                return false;
            }
        }

        /// <summary>
        /// 发送
        /// </summary>
        /// <param name="message"></param>
        /// <param name="encode"></param>
        public void Send(string message, Encoding encode)
        {
            Console.WriteLine("Begin Send message:{0}", message);
            byte[] bs = encode.GetBytes(message);
            this.Send(bs);
        }

        /// <summary>
        /// 发送
        /// </summary>
        /// <param name="bs"></param>
        public void Send(byte[] bs)
        {
            this.ProcessSend(bs, 0, bs.Length);
        }

        /// <summary>
        /// 接收数据
        /// </summary>
        public void ProcessRecieve()
        {
            if (!this._socket.Socket.Connected)
            {
                SeLog.Warn("连接已断开");
                this._socket.Socket.Disconnect(true);
                return;
            }
            SeLog.Info("尝试接收数据");

            SocketAsyncEventArgs receiveEventArgs;
            if (!this._receiveEventArgsPool.TryDequeue(out receiveEventArgs))
            {
                string errorMsg = "SocketClient->ProcessRecieve can't dequeue args from receiveeventargspool";
                SeLog.Error(errorMsg);
                throw new Exception(errorMsg);
            }
            if (receiveEventArgs.SocketError != SocketError.Success)
            {
                SeLog.Warn("连接已断开");
                return;
            }

            bool willRaiseEvent = this._socket.Socket.ReceiveAsync(receiveEventArgs);
            if (!willRaiseEvent)
                this.ReceiveEventArgs_Completed(null, receiveEventArgs);
        }

        private void ProcessSend(byte[] bytes, int offset, int length)
        {

            SocketAsyncEventArgs sendEventArgs;
            if (!this._sendEventArgsPool.TryDequeue(out sendEventArgs))
            {
                string errorMsg = "SocketClient->ProcessSend can't dequeue args from sendEventArgsPool";
                SeLog.Error(errorMsg);
                throw new Exception(errorMsg);
            }
            var bytesTitle = BitConverter.GetBytes(length);
            var data = new byte[length + bytesTitle.Length];
            Buffer.BlockCopy(bytesTitle, 0, data, 0, bytesTitle.Length);
            Buffer.BlockCopy(bytes, offset, data, bytesTitle.Length, length);
            sendEventArgs.SetBuffer(data, 0, data.Length);
            bool willRaiseEvent = this._socket.Socket.SendAsync(sendEventArgs);
            if (!willRaiseEvent)
                this.SendEventArgs_Completed(null, sendEventArgs);
        }

        private void SendEventArgs_Completed(object sender, SocketAsyncEventArgs args)
        {
            Console.WriteLine("Send Completed");
            //TODO 回收sendEventArgs
            this._sendEventArgsPool.Enqueue(args);
        }

        private void ReceiveEventArgs_Completed(object sender, SocketAsyncEventArgs args)
        {
            SeLog.Info("接收成功");

            //TODO 接收解析包头
            var doneBytes = 0;
            while ((args.BytesTransferred - doneBytes) > 4)
            {
                byte[] byteTitle = new byte[4];
                Buffer.BlockCopy(args.Buffer, args.Offset + doneBytes, byteTitle, 0, 4);
                doneBytes += 4;
                var byteLength = BitConverter.ToInt32(byteTitle, 0);
                if (args.BytesTransferred < byteLength + doneBytes)
                {
                    SeLog.Error("SocketListener->Receive_Completed 长度错误");
                    Disconnect();
                    return;
                }
                var byteContent = new byte[byteLength];
                Buffer.BlockCopy(args.Buffer, args.Offset + doneBytes, byteContent, 0, byteLength);
                this.OnReceiveData(byteContent);
                doneBytes += byteLength;
            }
            this.ProcessRecieve();

            this._receiveEventArgsPool.Enqueue(args);
        }

        private void Disconnect()
        {
            this.OnDisConnected(this._socket);
        }
    }
}
